package com.xmh.utils.automaticGeneration;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.annotation.Resource;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;

import com.mchange.v2.c3p0.ComboPooledDataSource;
import com.xmh.utils.FileUtil;

/**
 * 代码自动生成 --- spring boot jpa
 * 
 * @author
 *
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/spring_bean.xml")
@Transactional
public class AutomaticCodeGenerationByJPA {

	private final String vo_Path = "F:\\eclipseWord2\\file\\dandy\\dandy-database\\src\\main\\java\\com\\zlhy\\dandy\\database\\entity\\";

	private final String dao_Path = "F:\\eclipseWord2\\file\\dandy\\dandy-database\\src\\main\\java\\com\\zlhy\\dandy\\database\\jpa\\";

	private final String controller_Path = "F:\\eclipseWord2\\file\\dandy\\dandy-business\\src\\main\\java\\com\\zlhy\\dandy\\business\\controller\\";

	private final String service_Path = "F:\\eclipseWord2\\file\\dandy\\dandy-business\\src\\main\\java\\com\\zlhy\\dandy\\business\\service\\";

	private final String[] table_Name_List = { "t_withdraw" };

	@Resource(name = "dataSource")
	private ComboPooledDataSource comboPooledDataSource;

	private final String controle_Code = FileUtil
			.getFileStr(this.getClass().getResource("/model/JPA/controleModel.model").getPath());

	private final String service_Impl_Code = FileUtil
			.getFileStr(this.getClass().getResource("/model/JPA/serviceImplementsModel.model").getPath());

	private final String service_Inte_Code = FileUtil
			.getFileStr(this.getClass().getResource("/model/JPA/serviceInterfaceModel.model").getPath());

	private final String dao_Inte_Code = FileUtil
			.getFileStr(this.getClass().getResource("/model/JPA/daoInterface.model").getPath());

	@Test
	public void create() throws Exception {
		List<Object> tableList = selectTalbeDetailed(false, table_Name_List);

		createVo(tableList);
		createMode(tableList, dao_Inte_Code, dao_Path, "Repository");
		createMode(tableList, controle_Code, controller_Path, "Controller");
		createMode(tableList, service_Inte_Code, service_Path, "Service");
		createMode(tableList, service_Impl_Code, service_Path, "ServiceImpl");
	}

	/**
	 * 根据模板文件创建对应的文件以及对应路径
	 * 
	 * @param tableList
	 * @param modeCode
	 * @param path
	 * @param nameSuffix
	 */
	@SuppressWarnings("unchecked")
	private void createMode(List<Object> tableList, String modeCode, String path, String nameSuffix) {
		for (Object object : tableList) {
			Map<String, Object> tableMap = (Map<String, Object>) object;
			// 转为驼峰
			String voName = nameTransformation((String) tableMap.get("tableName"));
			// 首字母大写
			String className = titleCase(voName);

			String filePath = path;
			String fileName = className + nameSuffix;

			filePath += fileName.substring(fileName.length() - 4).equals("Impl") ? "\\impl\\" : "";

			String code = extracted(voName, className, modeCode);

			outFile(code, filePath, fileName, "java");
		}
	}

	/**
	 * 
	 * 替换对应的包名-类型，以及表名
	 * 
	 * @param voName
	 * @param className
	 * @param code
	 * @return
	 */
	private String extracted(String voName, String className, String code) {
		String nowStr = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());

		String replace = code.replace("______y______", voName);
		replace = replace.replace("_____X______", className);
		replace = replace.replace("____Date____", nowStr);
		replace = replace.replace("___EPath___", getPackagePath(vo_Path));
		replace = replace.replace("___SPath___", getPackagePath(service_Path));
		replace = replace.replace("___DPath___", getPackagePath(dao_Path));
		replace = replace.replace("___CPath___", getPackagePath(controller_Path));

		return replace;
	}

	/**
	 * 根据文件相对路径获取包路径
	 * 
	 * @param path
	 * @return
	 */
	private String getPackagePath(String path) {
		String str = path.split("src")[1];
		String substring = str.substring(1, str.length() - 1);

		if (!substring.startsWith("com")) {
			String string = path.split("java")[1];
			substring = string.substring(1, string.length() - 1);
		}

		String packPach = substring.replace("\\", ".");
		return packPach;
	}

	/**
	 * 创建实体类
	 * 
	 * @param tableNameMaplist
	 */
	@SuppressWarnings("unchecked")
	private void createVo(List<Object> tableNameMaplist) {
		for (Object object : tableNameMaplist) {
			Map<String, Object> tableMap = (Map<String, Object>) object;

			String tableName = (String) tableMap.get("tableName");
			// 转为驼峰
			String voName = nameTransformation(tableName);
			// 首字母大写
			String className = titleCase(voName) + "Entity";

			StringBuffer voClassFileSb = new StringBuffer();

			voClassFileSb.append("package " + getPackagePath(vo_Path) + ";\n\n");
			voClassFileSb.append("import java.util.Date;\n");
			voClassFileSb.append("import java.math.BigDecimal;\n");
			voClassFileSb.append("import java.sql.Timestamp;\n");
			voClassFileSb.append("import javax.persistence.Entity;\n");
			voClassFileSb.append("import javax.persistence.GeneratedValue;\n");
			voClassFileSb.append("import javax.persistence.GenerationType;\n");
			voClassFileSb.append("import lombok.Data;\n");
			voClassFileSb.append("import javax.persistence.Id;\n");
			voClassFileSb.append("import javax.persistence.Table;\n");
			voClassFileSb.append("import java.io.Serializable;\n\n");

			voClassFileSb.append("import javax.persistence.Temporal;\n");
			voClassFileSb.append("import javax.persistence.TemporalType;\n");
			voClassFileSb.append("import org.hibernate.annotations.CreationTimestamp;\n");
			voClassFileSb.append("import org.hibernate.annotations.UpdateTimestamp;\n\n");

			voClassFileSb.append("@Data\n");
			voClassFileSb.append("@Entity\n");
			voClassFileSb.append("@Table(name = \"" + tableName + "\")\n");
			voClassFileSb.append("public class " + className + " implements Serializable {\n\n");

			long nextLong = new Random(System.currentTimeMillis()).nextLong();

			voClassFileSb.append("	/**\n");
			voClassFileSb.append("	 * 版本号\n");
			voClassFileSb.append("	 */\n");
			voClassFileSb.append("	private static final long serialVersionUID = " + nextLong + "L;\n\n");

			List<Object> list = (List<Object>) tableMap.get("columnList");
			for (int i = 0; i < list.size(); i++) {
				Map<String, Object> columnMap = (Map<String, Object>) list.get(i);

				String columnName = (String) columnMap.get("columnName");
				// 字段名转为驼峰
				String cName = nameTransformation(columnName);

				String remarks = (String) columnMap.get("remarks");

				// 转为对应的java类型
				String javaType = typeTransformation((String) columnMap.get("type"));

				voClassFileSb.append("	/**\n	 *" + remarks + "\n	 */\n");

				if (i == 0) {
					voClassFileSb.append("	@Id\n");
					voClassFileSb.append("	@GeneratedValue(strategy = GenerationType.IDENTITY)\n");
				}
				if ("updateDate".equals(cName)) {
					voClassFileSb.append("    @Temporal(TemporalType.TIMESTAMP)\n");
					voClassFileSb.append("    @UpdateTimestamp\n");
				}
				if ("createDate".equals(cName)) {
					voClassFileSb.append("    @Temporal(TemporalType.TIMESTAMP)\n");
					voClassFileSb.append("    @CreationTimestamp\n");
				}

				voClassFileSb.append("	private " + javaType + " " + cName + ";\n\n");
			}

			voClassFileSb.append("\n}");

			outFile(voClassFileSb.toString(), vo_Path, className, "java");
		}
	}

	private String typeTransformation(String type) {
		if (type.equals("INT") || type.equals("TINYINT") || type.equals("SMALLINT")) {
			return "Integer";
		} else if (type.equals("VARCHAR") || type.equals("TEXT")) {
			return "String";
		} else if (type.equals("BIGINT") || type.equals("BIGINT UNSIGNED")) {
			return "Long";
		} else if (type.equals("BIT")) {
			return "Boolean";
		} else if (type.equals("FLOAT")) {
			return "Float";
		} else if (type.equals("DOUBLE")) {
			return "Double";
		} else if (type.equals("TIMESTAMP")) {
			return "Timestamp";
		} else if (type.equals("DECIMAL") || type.equals("DECIMAL UNSIGNED")) {
			return "BigDecimal";
		} else if (type.equals("DATETIME") || type.equals("DATE")) {
			return "Date";
		}
		return type;
	}

	/**
	 * 根据表名获取到表格的详细数据
	 * 
	 * @param tableNames
	 * @return
	 * @throws SQLException
	 */
	private List<Object> selectTalbeDetailed(boolean isAll, String... tableNames) throws SQLException {
		List<Object> tableNameMaplist = new ArrayList<Object>();

		// 获得元数据
		DatabaseMetaData metaData = comboPooledDataSource.getConnection().getMetaData();

		List<String> tableNameList = new ArrayList<String>(Arrays.asList(tableNames));

		if (isAll) {
			tableNameList.clear();
			ResultSet tables = metaData.getTables(null, null, null, new String[] { "TABLE" });
			while (tables.next()) {
				tableNameList.add(tables.getString("TABLE_NAME"));
			}
		}

		for (String talbeName : tableNameList) {
			// 通过表名获得所有字段名
			ResultSet columns = metaData.getColumns(null, null, talbeName, "%");

			List<Object> list = new ArrayList<Object>();

			// 获得所有字段名
			while (columns.next()) {
				Map<String, String> columnNameMap = new HashMap<>(); // 保存字段名

				columnNameMap.put("type", columns.getString("TYPE_NAME"));
				columnNameMap.put("columnName", columns.getString("COLUMN_NAME"));
				columnNameMap.put("remarks", columns.getString("REMARKS"));

				list.add(columnNameMap);
			}
			Map<String, Object> tableNameMap = new HashMap<>();
			tableNameMap.put("tableName", talbeName);
			tableNameMap.put("columnList", list);
			tableNameMaplist.add(tableNameMap);
		}

		return tableNameMaplist;
	}

	/**
	 * 输出文件到指定目录文件夹
	 * 
	 * @param code
	 * @param path
	 * @param fileName
	 * @param fileType
	 */
	private void outFile(String code, String path, String fileName, String fileType) {
		try {
			File file = new File(path + "//" + fileName + "." + fileType);

			if (!file.exists()) {
				new File(path).mkdirs();
				file.createNewFile();
			}
			OutputStreamWriter write = new OutputStreamWriter(new FileOutputStream(file), "UTF-8");
			BufferedWriter writer = new BufferedWriter(write);
			writer.write(code);
			writer.flush();
			writer.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	private String nameTransformation(String name) {
		if (name.indexOf("T_") == 0 || name.indexOf("t_") == 0) {
			name = name.substring(2, name.length());
		}

		Pattern linePattern = Pattern.compile("_(\\w)");
		name = name.toLowerCase();
		Matcher matcher = linePattern.matcher(name);
		StringBuffer sb = new StringBuffer();
		while (matcher.find()) {
			matcher.appendReplacement(sb, matcher.group(1).toUpperCase());
		}
		matcher.appendTail(sb);
		String string = sb.toString();

		return string;

	}

	/**
	 * 首字母大写
	 * 
	 * @param string
	 * @return
	 */
	private String titleCase(String string) {
		return string.substring(0, 1).toUpperCase() + string.substring(1, string.length());
	}
}
